using System;
using System.Drawing;
using MathService;

namespace GDI.Scales
{
    public class LinearScale : PrintableItem
    {
        /* -------------------------------------------------------------------------------------------- */

        private PointF f_StartPos;
        private PointF f_StopPos;

        private float f_LineStep;
        private float f_LinesWidth = 1;
        private float f_Angle;

        private double f_StartValue = 0;
        private double f_StopValue = 1;

        private Pen f_LinePen = Pens.Black;
        private float f_LineLength;
        private int f_LinesCount;
        private PointF f_BaseOffsetPosition;
        private PointF f_PostOffsetPosition;
        private bool f_Inversed;

        /* -------------------------------------------------------------------------------------------- */

        public bool Inversed { get { return f_Inversed; } set { f_Inversed = value; OnPropertyChanged(); } }

        public Interval Value
        {
            get { return new Interval(f_StartValue, f_StopValue); }
            set
            {
                StartValue = value.Min;
                StopValue = value.Max;
            }
        }
        public double StartValue { get { return f_StartValue; } set { f_StartValue = value; OnPropertyChanged(); } }
        public double StopValue { get { return f_StopValue; } set { f_StopValue = value; OnPropertyChanged(); } }

        public double DeltaValue { get { return f_StopValue - f_StartValue; } }

        public PointF StartPos { get { return f_StartPos; } set { f_StartPos = value; OnPropertyChanged(); } }
        public PointF StopPos { get { return f_StopPos; } set { f_StopPos = value; OnPropertyChanged(); } }

        public SizeF Size { get { return new SizeF(f_StopPos.X - f_StartPos.X, f_StopPos.Y - f_StartPos.Y); } }

        public float Length
        {
            get
            {
                var lv_Size = Size;
                return (float)Math.Sqrt(lv_Size.Width * lv_Size.Width +
                                        lv_Size.Height * lv_Size.Height);
            }
        }

        public int LinesCount { get { return f_LinesCount; } set { f_LinesCount = value; OnPropertyChanged(); } }

        public float LineStep { get { return Length / (LinesCount - 1); } }

        public float LineLength { get { return f_LineLength; } set { f_LineLength = value; OnPropertyChanged(); } }

        public float LinesWidth { get { return f_LinesWidth; } set { f_LinesWidth = value; OnPropertyChanged(); } }

        public new double Angle { get { return Math.Atan2(StopPos.Y - StartPos.Y, StopPos.X - StartPos.X); } }

        public double dx { get { return DeltaValue / Length; } }

        public Pen LinePen { get { return f_LinePen; } set { f_LinePen = value; OnPropertyChanged(); } }

        public PointF BaseOffsetPosition { get { return f_BaseOffsetPosition; } set { f_BaseOffsetPosition = value; OnPropertyChanged(); } }
        public PointF PostOffsetPosition { get { return f_PostOffsetPosition; } set { f_PostOffsetPosition = value; OnPropertyChanged(); } }

        /* -------------------------------------------------------------------------------------------- */

        public LinearScale() { }

        public LinearScale(PointF Start, PointF Stop)
        {
            f_StartPos = Start;
            f_StopPos = Stop;
        }

        /* -------------------------------------------------------------------------------------------- */

        public void SetPositions(PointF Start, PointF Stop)
        {
            f_StartPos = Start;
            f_StopPos = Stop;

        }

        public PointF ToScreen(double value)
        {
            var v = value - f_StartValue;
            var a = Angle;
            var sin = Math.Sin(a);
            var cos = Math.Cos(a);
            var dx = this.dx;
            var vx = v * cos / dx;
            var vy = v * sin / dx;
            return new PointF((float)vx + f_StartPos.X, (float)vy + f_StartPos.Y);
        }

        //public float ToScreen(double PrimaryValue, double SecundaryValue)
        //{
        //    var v = new Vector2D(PrimaryValue, SecundaryValue);
        //    var scale = (Vector2D)Complex.Exp(Length, Angle);
        //    return (float)(v * scale);
        //}

        public float ValueToScreen(double Value) { return (float)((Value - f_StartValue) / dx); }
        public double ScreenToValue(float Position, float offset = 0) { return (Position - offset) * dx + f_StartValue; }

        public double FromScreen(PointF p)
        {
            var p1 = p.Substract(StartPos);
            var p0 = StopPos.Substract(StartPos);
            var L = Length;
            var D = DeltaValue;
            var dl = D / L;
            double v = (p0.X * p1.X + p0.Y * p1.Y) / L;
            v *= dl;
            return v + f_StartValue;
        }

        public void Autoscale()
        {
            var ValueLength = DeltaValue;
            var logValuelength = Math.Log10(Math.Abs(ValueLength));
            var Value_dx = Math.Pow(10, logValuelength > 0 ? (int)(logValuelength) : (int)(logValuelength) - 1);
            if(Math.Abs(Value_dx - ValueLength) < 0.0001)
            {
                logValuelength -= 0.5;
                Value_dx = Math.Pow(10, logValuelength > 0 ? (int)(logValuelength) : (int)(logValuelength) - 1);
            }
            var N = (int)(ValueLength / Value_dx);
            //var ScreenLength = Length;
            LinesCount = N;
            //LineStep = ScreenLength / N;
            //var dx = ValueLength / ScreenLength;
        }

        protected override void Draw(Graphics g)
        {
            //var IsRotated = !f_Angle.Equals(0);
            //if(IsRotated)
            //    g.RotateTransform(f_Angle);

            var x = StartPos.X;
            var y = StartPos.Y;
            var N = LinesCount;
            var d = Length / N;
            var a = Angle;
            var sin = Math.Sin(a);
            var cos = Math.Cos(a);
            var dx = (float)(d * cos);
            var dy = (float)(d * sin);
            //var dx0 = (float)(f_BaseOffsetPosition.X*cos);
            var l = LineLength;
            var Lcos = l / 2 * Math.Cos(a + Math.PI / 2);
            var Lsin = l / 2 * Math.Sin(a + Math.PI / 2);
            for(var i = 0; i <= N; i++)
            {
                var x1 = (float)(x + Lcos) + f_PostOffsetPosition.X;
                var y1 = (float)(y + Lsin) + f_PostOffsetPosition.Y;
                var x2 = (float)(x - Lcos) + f_PostOffsetPosition.X;
                var y2 = (float)(y - Lsin) + f_PostOffsetPosition.Y;

                g.DrawLine(f_LinePen, x1, y1, x2, y2);

                x += dx;
                y += dy;
            }

            //if(IsRotated)
            //    g.ResetTransform();
        }

        /* -------------------------------------------------------------------------------------------- */



        /* -------------------------------------------------------------------------------------------- */
    }
}